Read Buf

Read Buf

gRPC 中不好的部分

gRPC,这个高性能的RPC框架,在Google内部取得了巨大的成功,并深刻地改变了API的部署方式。虽然它与protobuf一起构成了一个高效、契约导向的框架,并支持多种编程语言,但也存在一些不足。随着gRPC使用近十年,我们有必要回顾一下可以改进的地方。

pic

学习曲线

我们先挑些小问题开始。所谓的一元RPC(Unary RPC)指的是客户端发送一个请求,然后接收一个响应。为什么gRPC要用这样一个只有数学家才会理解的术语呢?每次用这个词,我都得多解释几句,真的有点烦。

说到一元RPC,其实现比实际需要的复杂。虽然gRPC的流处理能力很强,但在不需要流的简单RPC调用中也引入了不必要的复杂性。这让我们在检查gRPC调用时更加困难,因为每个一元RPC都加上了仅在流传输时才有意义的框架。Protobuf的编码已经足够复杂了,不要再添加额外的gRPC框架了。此外,这种复杂性也让gRPC不符合“能发给朋友一个cURL示例”的测试标准。每次解释gRPC时,我都得问“服务器反射功能启用了吗?”这真的很烦。

这种复杂性也影响到了工具使用,特别是强制性的代码生成步骤。这对那些偏好动态语言和运行时灵活性的开发者来说是一个障碍。此外,一些开发者可能会犹豫是否采用需要额外构建步骤的技术。在现代web开发中,我们已经需要多个构建步骤,再加一个很难让人接受。

与Web的兼容性

gRPC最初依赖HTTP/2,这限制了其在不同平台和浏览器上的适用性。虽然随着时间推移情况有所改善,但在某些环境中仍然存在挑战。即使现在支持HTTP/2的浏览器也没有处理HTTP trailers的功能,因此仍无法使用“原生”gRPC。gRPC-Web通过避免使用trailers来暂时解决了这个问题,但这通常需要额外的配置,比如设置支持gRPC-Web的代理服务器,确实有点麻烦。

迟迟不支持HTTP/3:延迟采用HTTP/3可能影响了gRPC充分利用该协议的性能和效率。我个人也曾因使用HTTP/2时遇到的“队头阻塞”问题而感到困扰,如果能用HTTP/3彻底解决这个问题,那就太好了。令人奇怪的是,一个曾推动多种语言支持HTTP/2的框架,在采用HTTP/3时却面临困难。

JSON映射与Prototext

在标准化JSON映射的时机上,gRPC错失了良机。这让习惯于JSON API的开发者对gRPC不太友好,我觉得它一直未能摆脱这种印象。通过在protobuf类型与JSON之间建立映射,可以简化与现有工具和系统的集成。当你告诉开发者“这是一个非常高效的二进制格式,但如果想调试,可以设置标志返回JSON”时,他们会非常高兴。总之,现在protobuf已经有了标准的JSON映射规则,我觉得prototext格式已经没有必要了。既然有了JSON,我看不到再使用文本格式的理由。所以,我们就放弃文本格式吧。如果大家同意,我就当它从未存在。好吗?

有限的消息大小

大多数Protobuf编码器/解码器期望能完全解析整个消息并将完整响应返回给用户,但内存有限,有时你可能需要处理更大的消息。你可能希望将这些大型消息的部分数据流式传输出去,而不是全部保存在内存中。因此,如果你想上传大文件,需要实现某种分块方案。虽然分块是处理大文件的合理解决方案,但gRPC缺乏标准化的方法,可能导致不同实现之间不一致,并增加开发工作量。

以下是使用gRPC上传文件的示例:

syntax = "proto3";

package file_service;

service FileService {
   rpc Upload(stream UploadRequest) returns(UploadResponse);
}

message UploadRequest {
    string file_name = 1;
    bytes chunk = 2;
}

message UploadResponse {
  string etag = 1;
}

这既是protobuf的优势,也是其劣势。在protobuf中定义这些概念非常简单,但实际实现起来,代码会显得繁琐且容易出错。虽然gRPC的创建者Google已经为他们的API找到了有效的解决方案,但缺乏标准化的做法使其他人不得不重新摸索。

你可能会想,“Google在大多数API中都使用gRPC,所以他们肯定有解决方案”,这确实如此。他们有一个用于下载大文件的gRPC和HTTP版本。直接比较这两个版本,可以看到gRPC复杂得多。可以查看链接中的代码,您会发现区别。我等着您比较。

沉寂的互联网理论

我发现很多gRPC和protobuf社区活动较少。一些网站上缺乏明显的讨论和活动,可能会让人觉得gRPC不再发展或维护不积极。这可能会阻碍新用户的加入,导致社区增长缓慢。或许是因为选择太多,导致很难在GitHub之外找到对gRPC感兴趣的人,而在GitHub问题区,这种热情可能会被视为烦人。

糟糕的工具支持

长期以来,当我看到某个代码库使用protobuf时,我会发现一个奇怪的脚本,它以非常自定义的方式下载各种protobuf文件,将它们放在随机路径中,然后对protoc进行复杂的调用。只有Google会认为不解决依赖管理问题就是解决了依赖管理问题。Google有自己高度“Google化”的依赖管理方式,而我们这些普通开发者只能羡慕。

它可以(并且确实)更好

虽然我对gRPC提出了一些批评,但希望这些评论被视为建设性的。读到这里的读者会知道,许多问题已经得到解决,或者正在解决的过程中!

  • 许多gRPC实现已经支持HTTP/3。ConnectRPC让使用HTTP/3变得非常简单(后续文章中会详细介绍)。

  • 由于protobuf规范提供了与JSON的标准映射,我不再需要担心文本格式。希望大家都能忘记它的存在。我们只有这么多文本格式可用。我不是在开玩笑,这是我最后一次提到它。

  • 如果你知道去哪里找,其实gRPC社区非常活跃。例如,buf的Slack频道是一个非常有用的资源。你可能经常会看到我在那里回答问题。

  • Buf CLI是一个出色的gRPC工具。它不仅完全取代了protoc,还增加了linting、断点检测、gRPC的curl、与Buf Schema Registry的集成(真正的依赖管理!)等功能。此外,许多你熟悉的HTTP工具如Postman、Insomnia和k6现在也支持gRPC。

尽管gRPC无疑取得了成功,但我们仍然需要正视它的不足,以确保其持续发展和改进。通过解决学习曲线、兼容性问题、缺乏标准化和社区参与等问题,我们可以释放gRPC的全部潜力,使其成为所有开发者更易于使用的工具。